﻿using SPWebShell.entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Web.Services.Protocols;

namespace SPWebShell.cmdlet
{
    /// <summary>
    /// Retrieves items from the specified List
    /// </summary>
    [Cmdlet(VerbsCommon.Get, "SPListItems")]
    public class GetSPListItemsCmdLet : CmdLetBase
    {

        public GetSPListItemsCmdLet()
        {
            this.MaxCount = 5000;
            this.Recursive = true;
        }
        [Parameter(Mandatory = true, HelpMessage = "The title of the List")]
        [ValidateNotNullOrEmpty]
        public string List { get; set; }
        
        [Parameter(Mandatory = true, HelpMessage="The absolute URL to the web site where the List is located or any item/view within the List. e.g. http://server/sites/SiteA/doclib1/Allitems.aspx")]
        [ValidateNotNullOrEmpty]
        public string Url { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "When IDMAX> 0 all items with ID>IDMIN and ID<IDMAX will be fetched upto a maximum of MaxCount items")]
        public Int32 IDMIN { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "When IDMAX> 0 all items with ID>IDMIN and ID<IDMAX will be fetched upto a maximum of MaxCount items")]
        public Int32 IDMAX { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "Specifies an upper limit on the total number of items fetched. Default=1000")]
        [Alias("count" , "max", "maxitems")]
        public Int32 MaxCount { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "Specifies an array of fields to fetch")]
        [Alias("Properties", "Property")]
        public string[] ViewFields { get; set; }
        
        [Parameter(Mandatory = false, HelpMessage = "When specified the underlying XML obtained from the web service is dumped to the file specified by property File. Useful for troubleshooting.")]
        public SwitchParameter Dump { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "When set to True all dates will be fetched relative to UTC time zone. Default is True")]
        [Alias("utc")]
        public bool DatesInUtc { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "When set to True all mandatory columns will be fetched. Default is True")]
        [Alias("mandatorycolumns")]
        public bool IncludeMandatoryColumns { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "The name of the view if caml query is not specified")]
        public string View { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "CAML query. This is used if View is not specified. E.g. '<Where><Lt><FieldRef Name='ID'/><Value Type='Counter'>3</Value></Lt></Where>' ")]
        [Alias("query")]
        public string Caml { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "The site relative folder to query. e.g. 'Shared Documents/folder1/folder2' ")]
        public string Folder { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "When set to True queries through all sub-folders recursively. Default is True")]
        [Alias( "recurse")]
        public bool  Recursive { get; set; }


        [Parameter(Mandatory = false, HelpMessage = "The name of the file where the raw contents will be optionally written to when Dump=True")]
        public string File { get; set; }

        protected override void ProcessRecord()
        {
            try
            {
                VerboseLogger("Start time: " + DateTime.Now.ToString());
                ISPRepository repos = base.SPRepos;
                SPDoclibLocation location = repos.ResolveDoclibUrlFromPageUrl(this.Url);
                //If a List name has been provided then use this name
                if (!string.IsNullOrEmpty(this.List)) location.DoclibName = this.List;
                entity.SPList listInfo = this.GetListInfo(location);
                PurifyFieldNames(listInfo);
                VerboseLogger("IDMIN:" + this.IDMIN);
                VerboseLogger("IDMAX:" + this.IDMAX);
                VerboseLogger(string.Format("CAML:" ,this.Caml) );
                VerboseLogger(string.Format("VIEW:", this.View));
                VerboseLogger(string.Format("VIEW:", this.View));
                VerboseLogger(string.Format("List Name:", this.List) );
                VerboseLogger(string.Format("URL:", this.Url) );
                VerboseLogger(string.Format("MaxCount:", this.MaxCount));

                //SPListItem[] items = repos.GetListItems(location, this.ID-1, this.MaxCount, this.ViewFields, this.Dump, this.File);
                if ((this.IDMAX > 0) && (this.IDMIN >= 0))
                {
                    VerboseLogger("Building caml to query for items between IDMAX and IDMIN");
                    this.Caml = this.GenerateCamlBetweenIDMinMax(this.IDMIN, this.IDMAX);
                }
                else
                {
                    VerboseLogger("Not using IDMAX and IDMIN to build the query because one of them was negative");
                }
                string dumpfile = "";
                if (this.Dump)
                {
                    dumpfile = base.GetFullPathToFile(this.File);
                }
                SPListItem[] items = repos.GetListItemsEx(
                        location, this.MaxCount, this.ViewFields, this.Caml, this.View,
                        this.DatesInUtc, this.Folder, this.IncludeMandatoryColumns, this.Recursive, this.Dump, dumpfile);
                foreach (SPListItem item in items)
                {
                    PSObject o1 = new PSObject();
                    o1.Members.Add(new PSNoteProperty("ID", item.SPID));
                    o1.Members.Add(new PSNoteProperty("Name", item.Name));
                    foreach (string fieldName in item.FieldValues.Keys)
                    {
                        Object fieldValue = item.FieldValues[fieldName];
                        o1.Members.Add(new PSNoteProperty(fieldName, fieldValue));
                    }
                    WriteObject(o1);
                }
                VerboseLogger("End time: " + DateTime.Now.ToString());
                VerboseLogger("Results - items obtained:" + items.Count());
                base.ProcessRecord();
            }
            catch (SoapException ex)
            {
                string message = ex.Detail.OuterXml;
                throw new Exception(message, ex);
            }
            finally
            {
                WriteVerbose("End time: " + DateTime.Now.ToString());
            }

        }
        string GenerateCamlBetweenIDMinMax(int idmin, int idmax)
        {
            string caml = "" +
                           "<Where>"+
                                "<And>"+
                                    "<Geq>" +
                                         "<FieldRef Name='ID' />" +
                                         "<Value Type='Counter'>{0}</Value>" +
                                    "</Geq>" +
                                    "<Leq>" +
                                         "<FieldRef Name='ID' />"+
                                         "<Value Type='Counter'>{1}</Value>" +
                                    "</Leq>"  +
                                "</And>" +
                           "</Where>" + 
                        "";
            caml = string.Format(caml, idmin, idmax);
            return caml;
        }
        entity.SPList GetListInfo(SPDoclibLocation listlocation)
        {
            this.VerboseLogger("Fetching metadata for the list:" + listlocation.DoclibName);
            entity.SPList listInfo = null;
            listInfo = base.SPRepos.GetList(listlocation);
            this.VerboseLogger("Metadata obtained:");
            return listInfo;            
        }
        private void PurifyFieldNames(entity.SPList listInfo)
        {
            if (this.ViewFields == null) return;
            if (this.ViewFields.Length == 0) return;
            List<string> purifiedpropnames = new List<string>();
            foreach (string propname in this.ViewFields)
            {
                if (string.IsNullOrEmpty(propname)) throw new Exception("Cannot accept NULL property names");
                string propnametrimmed = propname.Trim();
                entity.SPField thisField = listInfo.Fields.FirstOrDefault(f => (f.InternalName.Equals(propnametrimmed, StringComparison.CurrentCultureIgnoreCase)));
                if (thisField == null) throw new Exception(string.Format("No property found with the name:{0}", propnametrimmed));
                purifiedpropnames.Add(thisField.InternalName);
            }
            this.ViewFields = purifiedpropnames.ToArray();
        }
    }
}
